import { FrameworkConfigurator } from './framework-config';
import { EXTENSION_PATH, getExtensionConfig } from './utils';
import * as gift from 'tfig';

const gulp = require("gulp");
const ts = require("gulp-typescript");
const sourcemaps = require('gulp-sourcemaps');
const rename = require("gulp-rename");
const uglify = require("gulp-uglify");
const concat = require('gulp-concat');
const clean = require('gulp-clean');
const jsobfuscator = require('gulp-javascript-obfuscator');
const debug = require('gulp-debug');
const notify = require('gulp-notify');
const browserify = require('gulp-browserify');
const merge = require('merge2');
const through = require('through2');
const fs = require("fs");
const path = require("path");
const yargs = require('yargs');
const Path = require('path');

const fConfigFile = Path.join(EXTENSION_PATH, 'framework', 'config.json');

const econfig = getExtensionConfig();

/**
 * 清空资源框架
 */
function cleanAsset(cb) {
	if(fs.existsSync(`${EXTENSION_PATH}/framework`)){
		console.log("cleanAssetFramework");
		return gulp.src([ `${EXTENSION_PATH}/assets/scripts/${econfig.name}.js`, `${EXTENSION_PATH}/assets/scripts/${econfig.name}.d.ts`], { read: false, allowEmpty:true })
			.pipe(clean({ force:true }));
	}else{
		console.error("framework not found!");
		cb();
	}
}

/**
 * 预处理资源框架
 */
function preAsset(cb) {
	let fconfigor = new FrameworkConfigurator(fConfigFile);
	fconfigor.keepAllModules();
	fs.writeFileSync(`${EXTENSION_PATH}/framework/${fconfigor.getFramework()}.ts`, 
		`${fconfigor.collectExports().join('\n')}`, 
		{ encoding: 'utf8' });
	cb();
}

/**
 * 构建资源框架
 */
function buildAsset(cb) {
	if(fs.existsSync('framework')){
		let argv = yargs
			.default('project', '../../')
			.argv;
        
		let fconfigor = new FrameworkConfigurator(fConfigFile);
		fconfigor.keepAllModules();
		console.log("buildAssetFramework");
		let es6Result = gulp.src([ `${EXTENSION_PATH}/framework/**/*.ts`, `${EXTENSION_PATH}/@types/**/*.ts`, `${argv.project}/temp/declarations/**/*.ts` ])
            .pipe(ts.createProject(`${EXTENSION_PATH}/framework/tsconfig.json`, {
				target : "ES2015",
				outFile: `${econfig.name}-es6.js`
            })())
			.on("error",function(err:any){
				console.error(err.message);
			});
		let es5Result = gulp.src([ `${EXTENSION_PATH}/framework/**/*.ts`, `${EXTENSION_PATH}/@types/**/*.ts`, `${argv.project}/temp/declarations/**/*.ts` ])
            .pipe(ts.createProject(`${EXTENSION_PATH}/framework/tsconfig.json`, {
				target : "ES5",
				outFile: `${econfig.name}-es5.js`
            })())
			.on("error",function(err:any){
				console.error(err.message);
			});
		return merge(
			es6Result.js
				.pipe(browserify({
					insertGlobals : true,
					debug : true,
				}))
				.pipe(through.obj(function (chunk, enc, callback){
                    let sdata = chunk.contents.toString();
					for(let lib of fconfigor.collectLibs()){
						let libstr = fs.readFileSync(lib, 'utf8');
						sdata = sdata + '\n' + libstr;
					}
                    chunk.contents = Buffer.from(sdata);
                    this.push(chunk)
                    callback();
                }))
				.pipe(gulp.dest(`${EXTENSION_PATH}/temp`)),
			es5Result.js
				.pipe(browserify({
					insertGlobals : true,
					debug : true,
				}))
				.pipe(through.obj(function (chunk, enc, callback){
                    let sdata = chunk.contents.toString();
					for(let lib of fconfigor.collectLibs()){
						let libstr = fs.readFileSync(lib, 'utf8');
						sdata = sdata + '\n' + libstr;
					}
                    chunk.contents = Buffer.from(sdata);
                    this.push(chunk)
                    callback();
                }))
				.pipe(gulp.dest(`${EXTENSION_PATH}/temp`)),
			es6Result.dts
                .pipe(through.obj(function (chunk, enc, callback){
                    let sdata = chunk.contents.toString();
                    sdata = sdata.replace(new RegExp('/// <reference .*', "g" ),'');
                    sdata = sdata.replace('export let protocol: any;','// export let protocol: any;');
                    chunk.contents = Buffer.from(sdata);
                    this.push(chunk)
                    callback();
                }))
				.pipe(gulp.dest(`${EXTENSION_PATH}/temp`))
			);
	}else{
		console.error("framework not found!");
		cb();
	}
}

/**
 * 后处理框架资源
 */
async function postAsset(cb) {
	let fconfigor = new FrameworkConfigurator(fConfigFile);
	fconfigor.keepAllModules();

	// 合并代码
	fs.writeFileSync(`${EXTENSION_PATH}/assets/scripts/${econfig.name}.js`, 
		`
			if(globalThis.CC_EDITOR){
				${fs.readFileSync(`${EXTENSION_PATH}/temp/${econfig.name}-es6.js`, { encoding: 'utf8' })}
			}else{
				${fs.readFileSync(`${EXTENSION_PATH}/temp/${econfig.name}-es5.js`, { encoding: 'utf8' })}
			}
		`, { encoding: 'utf8' });
	
	// 简化声明
	const bundleResult = gift.bundle({
		input : `${EXTENSION_PATH}/temp/${econfig.name}-es6.d.ts`,
		name: 'lcc',
		entries : { lcc : 'lcc' },
		output : `${EXTENSION_PATH}/assets/scripts/${econfig.name}.d.ts`
	});
	await Promise.all(bundleResult.groups.map((group) => {
		fs.writeFileSync(group.path, 
			group.code.replace('\n',`\n\timport { ${fconfigor.collectCocosDeps().join(',')} } from "cc";\n`), 
			{ encoding: 'utf8' });
	}));
}

export const buildAssetFramework = gulp.series(cleanAsset, preAsset, buildAsset, postAsset);

/**
 * 预处理发布框架
 */
export function prePublish(cb){
	let argv = yargs
		.default('modules', "")
		.argv;
	let fconfigor = new FrameworkConfigurator(fConfigFile);
	fconfigor.keepModules(argv.modules.split(','));
	fs.writeFileSync(`${EXTENSION_PATH}/framework/${fconfigor.getFramework()}.ts`, 
		`${fconfigor.collectExports().join('\n')}`, 
		{ encoding: 'utf8' });
	cb();
}

/**
 * 构建发布框架
 */
export function buildPublish(cb) {
	if(fs.existsSync(`${EXTENSION_PATH}/framework`)){
		console.log("buildPublishFramework");
		let argv = yargs
			.default('project', '../../')
			.default('debug', false)
			.default('confuse', false)
			.default('modules', "")
			.default('outDir', "temp")
			.default('outFile', "framework-cache.js")
			.boolean(['debug', 'confuse'])
			.argv;
        
        // 计算编译源文件
        let fconfigor = new FrameworkConfigurator(fConfigFile);
        fconfigor.keepModules(argv.modules.split(','));
		let modules = fconfigor.collectModules();
        let sources = fconfigor.collectSources().map((item, index, input) => { return `${EXTENSION_PATH}/framework/${item}`; });
        sources.push(...[`${EXTENSION_PATH}/@types/**/*.ts`, `${argv.project}/temp/declarations/**/*.ts`]);
        
		let gulpState = gulp.src(sources);
		if(argv.debug){
			gulpState = gulpState.pipe(sourcemaps.init());
		}
		gulpState = gulpState.pipe(ts.createProject(`${EXTENSION_PATH}/framework/tsconfig.json`, { 
			target : "ES5",
			declaration : false,
			removeComments : true,
			outFile : argv.outFile,
		})())
		.on("error",function(err:any){
			console.error(err.message);
		});
		gulpState = gulpState.js;
		gulpState = gulpState.pipe(browserify({
			insertGlobals : true,
			debug : false,
		}))
		gulpState = gulpState.pipe(through.obj(function (chunk, enc, callback){
			let sdata = chunk.contents.toString();
			for(let lib of fconfigor.collectLibs()){
				let libstr = fs.readFileSync(lib, 'utf8');
				sdata = sdata + '\n' + libstr;
			}
			chunk.contents = Buffer.from(sdata);
			this.push(chunk)
			callback();
		}));
		if(argv.confuse){
			gulpState = gulpState.pipe(jsobfuscator({
				compact: true
			}));
		}
		gulpState = gulpState.pipe(uglify());
		if(argv.debug){
			gulpState = gulpState.pipe(sourcemaps.write());
		}
		return gulpState.pipe(gulp.dest(argv.outDir));
	}else{
		console.error("framework not found!");
		cb();
	}
}

export const buildPublishFramework = gulp.series(prePublish, buildPublish);
